﻿#include "StdAfx.h"
#include "eng_type.h"
#include "DBCEx.h"
#include "StmtEx.h"
#include "Globalvf.h"
#include "Lite_Util.h"
#include "sqlite3.h"
#include "LiteODBC.h"

#define VAL_COUNT  100;
#define WARR()    WriteArray("d:\\temp\\Arr.txt",__FUNCTION__,__LINE__);

#define GETTYPESQL "SELECT ORDER_IDX, TYPE_NAME , DATA_TYPE , PRECISION , LITERAL_PREFIX , LITERAL_SUFFIX , CREATE_PARAMS , \
                    NULLABLE , CASE_SENSITIVE , SEARCHABLE , UNSIGNED_ATTRIBUTE , MONEY , AUTO_INCREMENT , LOCAL_TYPE_NAME , \
					MINIMUM_SCALE , MAXIMUM_SCALE FROM TYPESPEC2_TBL ORDER BY  ORDER_IDX"


/**
	@briefn 생성자.
*/
CSTMTEx::CSTMTEx(const int nMyIndex, CDBCEx *pDbc)
{
	m_pDbc = pDbc; 
	m_bUse = TRUE;
	m_nColCount = 0;
	m_pStmt = NULL;
	m_guessed_types = 0;		/**< Flag for drv_prepare()/drvexecute() */
	memset(m_szlogmsg,0,1024);	
    memset(m_szSQLstate,0,6);
	m_pDynCols = NULL;
    nowchar[0] = m_pDbc->nowchar;
    nowchar[1] = 0;
    bkmrk = 0;			/**< True when bookmarks used */
	rowp = -1;
    retr_data = SQL_RD_ON;		/**< SQL_ATTR_RETRIEVE_DATA */
	bincache = NULL;
    binlen = 0;
	bincell = NULL;
    oemcp = &m_pDbc->oemcp;
	dobigint = pDbc->dobigint;
	m_bBind = FALSE;
	m_nCurType = SQL_CURSOR_FORWARD_ONLY;
	m_nDataSrc = DATA_SRC_NONE;
	//memset(m_pFirstData,0,sizeof(DATA_VAL));
	//m_pCurData = m_pFirstData;
	//m_pFirstData = NULL;
	//m_pCurData = NULL;
	//m_pInsData = NULL;
	m_nArrIdx = 0;
}

/**
	@briefn 소멸자.
*/
CSTMTEx::~CSTMTEx(void)
{
	if( NULL != m_pStmt)
		lite_CloseSTMT(m_pStmt);
	FreeDynCol();
	FreeData();
}

/**
	@brief 상태 설정 함수..
*/
void CSTMTEx::SetState(int naterr, char *msg, char *st, ...)
{
    va_list ap;
    naterr = naterr;
    m_szlogmsg[0] = '\0';
    if (msg) 
	{
		int count;
		va_start(ap, st);
		count = vsnprintf((char *) m_szlogmsg, sizeof (m_szlogmsg), msg, ap);
		va_end(ap);
		if (count < 0) 
		{
			m_szlogmsg[sizeof (m_szlogmsg) - 1] = '\0';
		}
    }
    if (!st) 
	{
		st = "?????";
    }
    strncpy_s(m_szSQLstate,6, st, 5);
    m_szSQLstate[5] = '\0';
}

/**
	@brief 재 사용을 데이터 초기화.
*/
void CSTMTEx::Reset()
{
	m_bUse = FALSE;
	if( NULL != m_pStmt)
	{
		lite_CloseSTMT(m_pStmt);
		m_pStmt = NULL;
	}
}

/**
	@brief Prepare.
*/
BOOL CSTMTEx::Prepare(const char * pszSQL)
{
	m_bBind = FALSE;
	m_nDataSrc = DATA_SRC_SERVER;
	if(NULL == m_pStmt)
	{
		m_pStmt = m_pDbc->CreateLiteSTMT();
	}
	if( TRUE == lite_Prepare(m_pStmt, pszSQL))
	{
		m_nColCount = lite_GetColCount(m_pStmt);
		SetupDynCols();
		FixupDynCols();
		return TRUE;
	}
	else
	{
		return FALSE;
	}
}

int CSTMTEx::Fetch()
{
	switch(m_nDataSrc)
	{
	case DATA_SRC_SERVER:
		{
			const char * pVal;
			if( TRUE == lite_MoveNext(m_pStmt))
			{
				for ( int i = 0; i < m_nColCount ; i ++ )
				{
					pVal = lite_GetValBuf(m_pStmt);
					STRCPY(m_pDynCols[i].szVal,pVal,4096);
				}
				if( TRUE == m_bBind)
				{
					return	DoFetchBind();
				}
				else
					return SQL_SUCCESS;
			}
			else
			{
				return SQL_NO_DATA;
			}
		}
	case DATA_SRC_LOCAL:
		{
			//if(  NULL == m_pCurData)
//				return SQL_NO_DATA;
			for (int i = 0 ; i < m_nColCount ; i ++ )
			{
				if( m_nArrIdx >= m_arrVal.GetCount())
					return SQL_NO_DATA;
				STRCPY(&m_pDynCols[i].szVal[1],m_arrVal[m_nArrIdx],4095);
				const char * ppp = &m_pDynCols[i].szVal[1];
				m_pDynCols[i].szVal[0] = 'T'; 
				m_nArrIdx ++;
			}
			if( TRUE == m_bBind)
			{
				return	DoFetchBind();
			}
			else
				return SQL_SUCCESS;
		}
	default:
		return SQL_ERROR;	
	}
}

int CSTMTEx::Fetch_org()
{
	const char * pVal;
	if( TRUE == lite_MoveNext(m_pStmt))
	{
		for ( int i = 0; i < m_nColCount ; i ++ )
		{
			pVal = lite_GetValBuf(m_pStmt);
			STRCPY(m_pDynCols[i].szVal,pVal,4096);
		}
		if( TRUE == m_bBind)
		{
			return	DoFetchBind();
		}
		else
			return SQL_SUCCESS;
	}
	else
	{
		return SQL_NO_DATA;
	}
}


BOOL CSTMTEx::SetupDynCols()
{
    int guessed_types = 0;
    SQLRETURN ret = SQL_SUCCESS;
	//==================================================
	// 컬럼이 0 보다 크다.
	//==================================================
	if( m_nColCount < 0)
		return FALSE;
	int i;
	const char *ptypename;
	FreeDynCol();
	XMALLOC(m_pDynCols,COLEX*,m_nColCount * sizeof(COLEX)) /// 메모리를 확보한다.
	GetColumnInfo(); /// 컬럼 정보를 가져 온다.
	for (i = 0; i < m_nColCount; i++) 
	{
		ptypename = StmtColType(i,&guessed_types);
		//m_pDynCols[i].db = m_pDbc->m_szDbName;
#ifdef SQL_LONGVARCHAR
		m_pDynCols[i].ColInfo.nType = SQL_LONGVARCHAR;
		m_pDynCols[i].ColInfo.nSize = 65535;
#else
		m_pDynCols[i].type = SQL_VARCHAR;
		m_pDynCols[i].size = 255;
#endif
		m_pDynCols[i].ColInfo.nIndex = i;
		m_pDynCols[i].ColInfo.nScale = 0;
		m_pDynCols[i].ColInfo.nPrec = 0;
		m_pDynCols[i].ColInfo.nNosign = 1;
#if defined(HAVE_SQLITE3TABLECOLUMNMETADATA) && HAVE_SQLITE3TABLECOLUMNMETADATA
		s3stmt_addmeta(s3stmt, i, d, &m_pDynCols[i]);
#else
		m_pDynCols[i].ColInfo.bAutoInc = SQL_FALSE;
		m_pDynCols[i].ColInfo.bNotNULL = SQL_NULLABLE;
#endif
		STRCPY(m_pDynCols[i].szTypeName,ptypename,16);
		// Bind를 위한 것.
		m_pDynCols[i].type = -1;
		m_pDynCols[i].max = 0;
		m_pDynCols[i].lenp = NULL;
		m_pDynCols[i].valp = NULL;
		m_pDynCols[i].index = i;
		m_pDynCols[i].offs = 0;
	}
	m_guessed_types = guessed_types;
	return TRUE;
}


const char * CSTMTEx::StmtColType(int col, int *guessed_types)
{
	char *ptypename = (char *)Get_Column_Decltype(col);
    char guess[64];
    guess[0] = '\0';
	if (!ptypename) 
	{
		int coltype = Get_Column_Type(col);
		if (guessed_types) 
		{
			guessed_types[0]++;
		}
		switch (coltype) 
		{
			case SQLITE_INTEGER: ptypename = "integer"; break;
			case SQLITE_FLOAT:   ptypename = "double";  break;
			default:
			case SQLITE_TEXT:    ptypename = "varchar"; break;
			case SQLITE_BLOB:    ptypename = "blob";    break;
#if 0
			case SQLITE_NULL:    ptypename = "null";    break;
#endif
		}
    }
    return ptypename;
}

/**
	@brief 컬럼 정보를 가져 온다.
*/
void CSTMTEx::GetColumnInfo()
{
	for ( int i = 0 ; i < m_nColCount ; i ++ )
	{
		lite_GetColInfo(m_pStmt,i,(Lite_ColInfo *)&m_pDynCols[i].ColInfo); 
	}
}

const char *CSTMTEx::Get_Column_Decltype(int N)
{
	if((N < 0) || (m_nColCount <= N))
	{
		return SPACECHAR;
	}
	if( strlen(m_pDynCols[N].ColInfo.szTypename) < 1 )
		return NULL;
	else 
		return m_pDynCols[N].ColInfo.szTypename;
}


int CSTMTEx::Get_Column_Type(int N)
{
	if((N < 0) || (m_nColCount <= N))
	{
		return 0;
	}
	return m_pDynCols[N].szVal[0];
}

void CSTMTEx::FreeDynCol()
{
	if( NULL != m_pDynCols)
		free(m_pDynCols);
	m_pDynCols = NULL;
}

void CSTMTEx::FreeData()
{
	/*
	DATA_VAL *pNext = NULL;
	m_pCurData = m_pFirstData;
	while(m_pCurData)
	{
		XFREE(m_pCurData->pVal);
		pNext = m_pCurData->pNext;
		free(m_pCurData);
		m_pCurData = pNext;
	}
	*/
	for( int i = 0 ; i < m_arrVal.GetCount(); i ++)
	{
		free(m_arrVal[i]);
	}
}

void  CSTMTEx::ReadyFirstData()
{
	FreeData();
	//XMALLOC(m_pFirstData,DATA_VAL*,sizeof(DATA_VAL));
	//m_pCurData = m_pFirstData;
	//m_pInsData = m_pFirstData;
}

void  CSTMTEx::SetLocalData( BOOL bBool)
{
	m_nDataSrc = bBool? DATA_SRC_LOCAL : DATA_SRC_SERVER;
}

int CSTMTEx::Execute()
{
	return SQL_SUCCESS;
}

int CSTMTEx::DescribeCol(SQLUSMALLINT col, SQLCHAR * name,
	       SQLSMALLINT nameMax, SQLSMALLINT *nameLen,
	       SQLSMALLINT *type, SQLULEN *size,
	       SQLSMALLINT *digits, SQLSMALLINT *nullable)
{
    int didname = 0;
	if( 0 >= m_nColCount)
	{
		SetState( -1, "no columns", g_ov3 ? "07009" : "S1002");
		return SQL_ERROR;
    }
    if (col < 1 || col > m_nColCount) 
	{
		SetState(-1, "invalid column", g_ov3 ? "07009" : "S1002");
		return SQL_ERROR;
    }
	col--;
    if (name && nameMax > 0) 
	{
		//util_UTF2UC(c->column,name,nameMax);
		strncpy((char *) name, m_pDynCols[col].ColInfo.szColumnName, nameMax);
		name[nameMax - 1] = '\0';
		didname = 1;
    }
    if (nameLen) 
	{
		if (didname) 
		{
			*nameLen = strlen((char *) name);
		} 
		else 
		{
			*nameLen = strlen(m_pDynCols[col].ColInfo.szColumnName);
		}
    }
	if (nowchar[0] || nowchar[1]) 
	{
	    switch (m_pDynCols[col].ColInfo.nType) 
		{
		case SQL_WCHAR:
			*type = SQL_CHAR;
			break;
		case SQL_WVARCHAR:
			*type = SQL_VARCHAR;
			break;
		case SQL_WLONGVARCHAR:
			*type = SQL_LONGVARCHAR;
			break;
		}
	}
    if (size) 
	{
		*size = m_pDynCols[col].ColInfo.nSize;
    }
    if (digits) 
	{
		*digits = 0;
    }
    if (nullable) 
	{
		*nullable = 1;
    }
    return SQL_SUCCESS;
}


int CSTMTEx::NoMemory()
{
	SetState(-1, "Out of memory", g_ov3 ? "HY000" : "S1000");
    return SQL_ERROR;
}

int CSTMTEx::GetRowData(SQLUSMALLINT col, SQLSMALLINT otype, SQLPOINTER val, SQLINTEGER len, SQLLEN *lenp, int partial)
{
    char *pszData, valdummy[16];
    SQLLEN dummy;
    int valnull = 0;
    int type = otype;

    if (!lenp) 
	{
		lenp = &dummy;
    }
    if (col >= m_nColCount) 
	{
		SetState( -1, "invalid column", g_ov3 ? "07009" : "S1002");
		return SQL_ERROR;
    }
    if (retr_data != SQL_RD_ON) 
	{
		return SQL_SUCCESS;
    }
	type = mapdeftype(type, m_pDynCols[col].ColInfo.nType, m_pDynCols[col].ColInfo.nNosign ? 1 : 0, nowchar[0]);
#if (defined(_WIN32) || defined(_WIN64)) && defined(WINTERFACE)
    /* MS Access hack part 3 (map SQL_C_DEFAULT to SQL_C_CHAR) */
    if (type == SQL_C_WCHAR && otype == SQL_C_DEFAULT) 
	{
		type = SQL_C_CHAR;
    }
#endif
	pszData = & m_pDynCols[col].szVal[1];
    if (!val) 
	{
		valnull = 1;
		val = (SQLPOINTER) valdummy;
    }
    if (pszData == NULL) 
	{
		*lenp = SQL_NULL_DATA;
		switch (type) 
		{
		case SQL_C_UTINYINT:
		case SQL_C_TINYINT:
		case SQL_C_STINYINT:
#ifdef SQL_BIT
		case SQL_C_BIT:
#endif
			*((char *) val) = 0;
			break;
		case SQL_C_USHORT:
		case SQL_C_SHORT:
		case SQL_C_SSHORT:
			*((short *) val) = 0;
			break;
		case SQL_C_ULONG:
		case SQL_C_LONG:
		case SQL_C_SLONG:
			*((SQLINTEGER *) val) = 0;
			break;
	#ifdef SQL_BIGINT
		case SQL_C_SBIGINT:
		case SQL_C_UBIGINT:
			*((SQLBIGINT *) val) = 0;
			break;
	#endif
		case SQL_C_FLOAT:
			*((float *) val) = 0;
			break;
		case SQL_C_DOUBLE:
			*((double *) val) = 0;
			break;
		case SQL_C_BINARY:
		case SQL_C_CHAR:
			*((char *) val) = '\0';
			break;
	#ifdef WCHARSUPPORT
		case SQL_C_WCHAR:
			*((SQLWCHAR *) val) = '\0';
			break;
	#endif
	#ifdef SQL_C_TYPE_DATE
		case SQL_C_TYPE_DATE:
	#endif
		case SQL_C_DATE:
			memset((DATE_STRUCT *) val, 0, sizeof (DATE_STRUCT));
			break;
	#ifdef SQL_C_TYPE_TIME
		case SQL_C_TYPE_TIME:
	#endif
		case SQL_C_TIME:
			memset((TIME_STRUCT *) val, 0, sizeof (TIME_STRUCT));
			break;
	#ifdef SQL_C_TYPE_TIMESTAMP
		case SQL_C_TYPE_TIMESTAMP:
	#endif
		case SQL_C_TIMESTAMP:
			memset((TIMESTAMP_STRUCT *) val, 0, sizeof (TIMESTAMP_STRUCT));
			break;
		default:
			return SQL_ERROR;
		}
    } 
	else 
	{
		char *endp = NULL;
#if defined(_WIN32) || defined(_WIN64)
#ifdef SQL_BIGINT
		char endc;
#endif
#endif

		switch (type) 
		{
		case SQL_C_UTINYINT:
		case SQL_C_TINYINT:
		case SQL_C_STINYINT:
			*((char *) val) = (char)strtol(pszData, &endp, 0);
			if (endp && endp == pszData) 
			{
				*lenp = SQL_NULL_DATA;
			} 
			else 
			{
				*lenp = sizeof (char);
			}
			break;
	#ifdef SQL_BIT
		case SQL_C_BIT:
			*((char *) val) = getbool(pszData);
			*lenp = sizeof (char);
			break;
	#endif
		case SQL_C_USHORT:
		case SQL_C_SHORT:
		case SQL_C_SSHORT:
			*((short *) val) = strtol(pszData, &endp, 0);
			if (endp && endp == pszData) {
			*lenp = SQL_NULL_DATA;
			} else {
			*lenp = sizeof (short);
			}
			break;
		case SQL_C_ULONG:
		case SQL_C_LONG:
		case SQL_C_SLONG:
			*((SQLINTEGER *) val) = strtol(pszData, &endp, 0);
			if (endp && endp == pszData) {
			*lenp = SQL_NULL_DATA;
			} else {
			*lenp = sizeof (SQLINTEGER);
			}
			break;
	#ifdef SQL_BIGINT
		case SQL_C_UBIGINT:
	#if defined(_WIN32) || defined(_WIN64)
			if (sscanf(pszData, "%I64u%c", (SQLUBIGINT *) val, &endc) != 1) {
			*lenp = SQL_NULL_DATA;
			} else {
			*lenp = sizeof (SQLUBIGINT);
			}
	#else
	#ifdef __osf__
			*((SQLUBIGINT *) val) = strtoul(pszData, &endp, 0);
	#else
			*((SQLUBIGINT *) val) = strtoull(pszData, &endp, 0);
	#endif
			if (endp && endp == pszData) {
			*lenp = SQL_NULL_DATA;
			} else {
			*lenp = sizeof (SQLUBIGINT);
			}
	#endif
			break;
		case SQL_C_SBIGINT:
	#if defined(_WIN32) || defined(_WIN64)
			if (sscanf(pszData, "%I64d%c", (SQLBIGINT *) val, &endc) != 1) {
			*lenp = SQL_NULL_DATA;
			} else {
			*lenp = sizeof (SQLBIGINT);
			}
	#else
	#ifdef __osf__
			*((SQLBIGINT *) val) = strtol(pszData, &endp, 0);
	#else
			*((SQLBIGINT *) val) = strtoll(pszData, &endp, 0);
	#endif
			if (endp && endp == pszData) {
			*lenp = SQL_NULL_DATA;
			} else {
			*lenp = sizeof (int);
			}
	#endif
			break;
	#endif
		case SQL_C_FLOAT:
			*((float *) val) = ln_strtod(pszData, &endp);
			if (endp && endp == pszData) {
			*lenp = SQL_NULL_DATA;
			} else {
			*lenp = sizeof (float);
			}
			break;
		case SQL_C_DOUBLE:
			*((double *) val) = ln_strtod(pszData, &endp);
			if (endp && endp == pszData) {
			*lenp = SQL_NULL_DATA;
			} else {
			*lenp = sizeof (double);
			}
			break;
		case SQL_C_BINARY: {
			int dlen, offs = 0;
			char *bin;

			if (valnull) {
			XFREE(bincache);
			binlen = 0;
			goto doCHAR;
			}
			if (pszData == bincell) {
			if (bincache) {
				bin = bincache;
				dlen = binlen;
			} else {
				goto doCHAR;
			}
			} else {
			char *dp;
			int i;

			XFREE(bincache);
			dp = pszData;
			dlen = strlen(dp);
			bincell = dp;
			binlen = 0;
			if (!(dp[0] == 'x' || dp[0] == 'X') || dp[1] != '\'' ||
				dp[dlen - 1] != '\'') {
				goto doCHAR;
			}
			dlen -= 2;
			dp += 2;
			dlen = dlen / 2;
			//bin = xmalloc(dlen);
			XMALLOC(bin,char *,dlen)
			bincache = bin;
			if (!bin) {
				return NoMemory();
			}
			binlen = dlen;
			memset(bincache, 0, dlen);
			for (i = 0; i < dlen; i++) {
				char *x;
				int v;

				if (!*dp || !(x = (char*)strchr(xdigits, *dp))) {
				goto converr;
				}
				v = x - xdigits;
				bin[i] = (v >= 16) ? ((v - 6) << 4) : (v << 4);
				++dp;
				if (!*dp || !(x = (char*)strchr(xdigits, *dp))) {
	converr:
				XFREE(bincache);
				binlen = 0;
				SetState( -1, "conversion error",
					g_ov3 ? "HY000" : "S1000");
				return SQL_ERROR;
				}
				v = x - xdigits;
				bin[i] |= (v >= 16) ? (v - 6) : v;
				++dp;
			}
			bin = bincache;
			}
			if (partial && len && m_bBind) {
			if (m_pDynCols[col].offs >= dlen) {
				*lenp = 0;
				if (!dlen && m_pDynCols[col].offs == dlen) {
				m_pDynCols[col].offs = 1;
				return SQL_SUCCESS;
				}
				m_pDynCols[col].offs = 0;
				return SQL_NO_DATA;
			}
			offs = m_pDynCols[col].offs;
			dlen -= offs;
			}
			if (val && len) {
			memcpy(val, bin + offs, min(len, dlen));
			}
			if (len < 1) {
			*lenp = dlen;
			} else {
			*lenp = min(len, dlen);
			if (*lenp == len && *lenp != dlen) {
				*lenp = SQL_NO_TOTAL;
			}
			}
			if (partial && len && m_bBind) {
			if (*lenp == SQL_NO_TOTAL) {
				*lenp = dlen;
				m_pDynCols[col].offs += len;
				SetState( -1, "data right truncated", "01004");
				if (m_pDynCols[col].lenp) {
				*m_pDynCols[col].lenp = dlen;
				}
				return SQL_SUCCESS_WITH_INFO;
			}
			m_pDynCols[col].offs += *lenp;
			}
			if (*lenp == SQL_NO_TOTAL) {
			*lenp = dlen;
			SetState( -1, "data right truncated", "01004");
			return SQL_SUCCESS_WITH_INFO;
			}
			break;
		}
		doCHAR:
	#ifdef WCHARSUPPORT
		case SQL_C_WCHAR:
	#endif
		case SQL_C_CHAR: {
			int doz, zlen = len - 1;
			int dlen = strlen(pszData);
			int offs = 0;
	#ifdef WCHARSUPPORT
			
	#endif

	#if (defined(_WIN32) || defined(_WIN64)) && defined(WINTERFACE)
			/* MS Access hack part 2 (reserved error -7748) */
			if (!valnull &&
			(m_pDynCols == statSpec2PEx || m_pDynCols == statSpec3PEx) &&
			type == SQL_C_WCHAR) {
			if (len > 0 && len <= sizeof (SQLWCHAR)) {
				((char *) val)[0] = pszData[0];
				memset((char *) val + 1, 0, len - 1);
				*lenp = 1;
				return SQL_SUCCESS;
			}
			}
	#endif

	#ifdef WCHARSUPPORT
			switch (type) {
			case SQL_C_CHAR:
			doz = 1;
			break;
			case SQL_C_WCHAR:
			doz = sizeof (SQLWCHAR);
			break;
			default:
			doz = 0;
			break;
			}
			if (type == SQL_C_WCHAR) 
			{
				//ucdata = uc_from_utf(cdata, dlen);
				memset(m_ucdata,0,4096*sizeof(SQLWCHAR));
				//dlen = uc_strlen(ucdata) * sizeof (SQLWCHAR);
				dlen = 4096;
			}
	#if defined(_WIN32) || defined(_WIN64)
			else if (*oemcp && type == SQL_C_CHAR) 
			{
				//ucdata = (SQLWCHAR *) utf_to_wmb((char *) cdata, dlen);
				//XMALLOC(ucdata,SQLWCHAR*,dlen*2)
				util_Char2WChar((char*)pszData,strlen((char*)pszData),m_ucdata,4096);
				dlen = strlen((char *)pszData);
			}
	#endif
	#else
			doz = (type == SQL_C_CHAR) ? 1 : 0;
	#endif
			if (partial && len && m_bBind) 
			{
				if (m_pDynCols[col].offs >= dlen) 
				{
					*lenp = 0;
					if (doz && val) 
					{
	#ifdef WCHARSUPPORT
						if (type == SQL_C_WCHAR) 
						{
							((SQLWCHAR *) val)[0] = 0;
						} 
						else 
						{
							((char *) val)[0] = '\0';
						}
	#else
						((char *) val)[0] = '\0';
	#endif
						}
						if (!dlen && m_pDynCols[col].offs == dlen) 
						{
							m_pDynCols[col].offs = 1;
							return SQL_SUCCESS;
						}
						m_pDynCols[col].offs = 0;
						return SQL_NO_DATA;
				}
				offs = m_pDynCols[col].offs;
				dlen -= offs;
			}
			if (val && !valnull && len) 
			{
	#ifdef WCHARSUPPORT
				if (type == SQL_C_WCHAR) 
				{
					//uc_strncpy((SQLWCHAR*)val, ucdata + offs / sizeof (SQLWCHAR),
					  //     (len - doz) / sizeof (SQLWCHAR)); ///???????????????????
					//strcpy(val,ucdata,sizeof(ucdata));
					wcsncpy((wchar_t*)val,m_ucdata, min((int)len,(int)wcslen(m_ucdata)));
				} 
				else 
				{
					strncpy((char*)val, (char *) pszData + offs, len - doz);
				}
	#else
				strncpy(val, data + offs, len - doz);
	#endif
			}
			if (valnull || len < 1) 
			{
				*lenp = dlen;
			} 
			else 
			{
				*lenp = min(len - doz, dlen);
				if (*lenp == len - doz && *lenp != dlen) 
				{
					*lenp = SQL_NO_TOTAL;
				} 
				else if (*lenp < zlen) 
				{
					zlen = *lenp;
				}	    
			}
			if (len && !valnull && doz) 
			{
	#ifdef WCHARSUPPORT
				if (type == SQL_C_WCHAR) 
				{
					((SQLWCHAR *) val)[zlen / sizeof (SQLWCHAR)] = 0;
				} 
				else 
				{
					((char *) val)[zlen] = '\0';
				}
	#else
				((char *) val)[zlen] = '\0';
	#endif
			}
			if (partial && len && m_bBind) 
			{
				if (*lenp == SQL_NO_TOTAL) 
				{
					*lenp = dlen;
					m_pDynCols[col].offs += len - doz;
					SetState( -1, "data right truncated", "01004");
					if (m_pDynCols[col].lenp) 
					{
						*m_pDynCols[col].lenp = dlen;
					}
					return SQL_SUCCESS_WITH_INFO;
				}
				m_pDynCols[col].offs += *lenp;
			}
			if (*lenp == SQL_NO_TOTAL) 
			{
				*lenp = dlen;
				SetState( -1, "data right truncated", "01004");
				return SQL_SUCCESS_WITH_INFO;
			}
			break;
		}
	#ifdef SQL_C_TYPE_DATE
		case SQL_C_TYPE_DATE:
	#endif
		case SQL_C_DATE:
			if (str2date(pszData, (DATE_STRUCT *) val) < 0) 
			{
				*lenp = SQL_NULL_DATA;
			} 
			else 
			{
				*lenp = sizeof (DATE_STRUCT);
			}
			break;
	#ifdef SQL_C_TYPE_TIME
		case SQL_C_TYPE_TIME:
	#endif
		case SQL_C_TIME:
			if (str2time(pszData, (TIME_STRUCT *) val) < 0) 
			{
				*lenp = SQL_NULL_DATA;
			} 
			else 
			{
				*lenp = sizeof (TIME_STRUCT);
			}
			break;
	#ifdef SQL_C_TYPE_TIMESTAMP
		case SQL_C_TYPE_TIMESTAMP:
	#endif
		case SQL_C_TIMESTAMP:
			if (str2timestamp(pszData, (TIMESTAMP_STRUCT *) val) < 0) 
			{
				*lenp = SQL_NULL_DATA;
			} 
			else 
			{
				*lenp = sizeof (TIMESTAMP_STRUCT);
			}
			switch (m_pDynCols[col].prec) 
			{
			case 0:
				((TIMESTAMP_STRUCT *) val)->fraction = 0;
				break;
			case 1:
				((TIMESTAMP_STRUCT *) val)->fraction /= 100000000;
				((TIMESTAMP_STRUCT *) val)->fraction *= 100000000;
				break;
			case 2:
				((TIMESTAMP_STRUCT *) val)->fraction /= 10000000;
				((TIMESTAMP_STRUCT *) val)->fraction *= 10000000;
				break;
			}
			break;
		default:
			return SQL_ERROR;
		}
    }
    return SQL_SUCCESS;
}

void CSTMTEx::FixupDynCols()
{
    int i;
#if !defined(HAVE_SQLITE3TABLECOLUMNMETADATA) || !HAVE_SQLITE3TABLECOLUMNMETADATA
    char *flagp, flags[128];
#endif

    if (!m_pDynCols) 
	{
		return;
    }
    /* fixup labels */
    if (!longnames) 
	{
		if (m_nColCount > 1) 
		{
			char *table = m_pDynCols[0].ColInfo.szTableName;
		    for (i = 1; table[0] && i < m_nColCount; i++) 
			{
				if (strcmp(m_pDynCols[i].ColInfo.szTableName, table)) 
				{
					break;
				}
			}
			if (i >= m_nColCount) 
			{
				for (i = 0; i < m_nColCount; i++) 
				{
					STRCPY(m_pDynCols[i].ColInfo.szLabel,m_pDynCols[i].ColInfo.szColumnName,128);
				}
			}
		} 
		else if (m_nColCount == 1) 
		{
			STRCPY(m_pDynCols[i].ColInfo.szLabel,m_pDynCols[i].ColInfo.szColumnName,128);
		}
    }
    for (i = 0; i < m_nColCount; i++) 
	{
		m_pDynCols[i].type =
			mapsqltype(m_pDynCols[i].szTypeName, &m_pDynCols[i].ColInfo.nNosign, g_ov3, nowchar[0] || nowchar[1], dobigint);
		getmd(m_pDynCols[i].szTypeName, m_pDynCols[i].type,	&m_pDynCols[i].ColInfo.nSize, &m_pDynCols[i].prec);
#ifdef SQL_LONGVARCHAR
		if (m_pDynCols[i].type == SQL_VARCHAR && m_pDynCols[i].ColInfo.nSize > 255) 
		{
			m_pDynCols[i].type = SQL_LONGVARCHAR;
		}
#endif
#ifdef WINTERFACE
#ifdef SQL_WLONGVARCHAR
		if (m_pDynCols[i].type == SQL_WVARCHAR &&
			m_pDynCols[i].ColInfo.nSize > 255) 
		{
			m_pDynCols[i].type = SQL_WLONGVARCHAR;
		}
#endif
#endif
		if (m_pDynCols[i].type == SQL_VARBINARY &&
			m_pDynCols[i].ColInfo.nSize > 255) 
		{
			m_pDynCols[i].type = SQL_LONGVARBINARY;
		}
    }
#if !defined(HAVE_SQLITE3TABLECOLUMNMETADATA) || !HAVE_SQLITE3TABLECOLUMNMETADATA
    if (m_nColCount > array_size(flags)) 
	{
		XMALLOC(flagp,char*,sizeof(flags[0]) * m_nColCount)
		if (flagp == NULL) 
		{
			return;
		}
    } 
	else 
	{
		flagp = flags;
    }
    memset(flagp, 0, sizeof (flags[0]) * m_nColCount);
    for (i = 0; i < m_nColCount; i++) 
	{
		m_pDynCols[i].ColInfo.bAutoInc = SQL_FALSE;
		m_pDynCols[i].ColInfo.bNotNULL = SQL_NULLABLE;
    }
    if (flagp != flags) 
	{
		XFREE(flagp);
    }
#endif
}

BOOL CSTMTEx::SetupLocalDynCols(int nType)
{
	m_nDataSrc = DATA_SRC_LOCAL;
	m_nArrIdx = 0;
	switch(nType)
	{
	case LOCAL_DYNSET_TABLLESPEC2:
		FreeDynCol();
		m_nColCount = 5;
		XMALLOC(m_pDynCols,COLEX*,m_nColCount * sizeof(COLEX)) /// 메모리를 확보한다.
		for( int i = 0 ; i < m_nColCount ; i ++)
		{
			STRCPY(m_pDynCols[i].ColInfo.szDbName ,tableSpec2[i].db,128);
			STRCPY(m_pDynCols[i].ColInfo.szTableName,tableSpec2[i].table,128);
			STRCPY(m_pDynCols[i].ColInfo.szColumnName,tableSpec2[i].column,128);
			m_pDynCols[i].ColInfo.nType = tableSpec2[i].type;
			m_pDynCols[i].type = -1;
			m_pDynCols[i].max = 0;
			m_pDynCols[i].lenp = NULL;
			m_pDynCols[i].valp = NULL;
			m_pDynCols[i].index = i;
			m_pDynCols[i].offs = 0;
		}
		break;
	case LOCAL_DYNSET_TABLLESPEC3:
		FreeDynCol();
		m_nColCount = 5;
		XMALLOC(m_pDynCols,COLEX*,m_nColCount * sizeof(COLEX)) /// 메모리를 확보한다.
		for( int i = 0 ; i < m_nColCount ; i ++)
		{
			STRCPY(m_pDynCols[i].ColInfo.szDbName ,tableSpec3[i].db,128);
			STRCPY(m_pDynCols[i].ColInfo.szTableName,tableSpec3[i].table,128);
			STRCPY(m_pDynCols[i].ColInfo.szColumnName,tableSpec3[i].column,128);
			m_pDynCols[i].ColInfo.nType = tableSpec2[i].type;
			m_pDynCols[i].type = -1;
			m_pDynCols[i].max = 0;
			m_pDynCols[i].lenp = NULL;
			m_pDynCols[i].valp = NULL;
			m_pDynCols[i].index = i;
			m_pDynCols[i].offs = 0;
		}
		break;
	case LOCAL_DYNSET_TYPESPEC2:
		FreeDynCol();
		m_nColCount = 15;
		XMALLOC(m_pDynCols,COLEX*,m_nColCount * sizeof(COLEX)) /// 메모리를 확보한다.
		for( int i = 0 ; i < m_nColCount ; i ++)
		{
			STRCPY(m_pDynCols[i].ColInfo.szDbName ,typeSpec2[i].db,128);
			STRCPY(m_pDynCols[i].ColInfo.szTableName,typeSpec2[i].table,128);
			STRCPY(m_pDynCols[i].ColInfo.szColumnName,typeSpec2[i].column,128);
			m_pDynCols[i].ColInfo.nType = tableSpec2[i].type;
			m_pDynCols[i].type = -1;
			m_pDynCols[i].max = 0;
			m_pDynCols[i].lenp = NULL;
			m_pDynCols[i].valp = NULL;
			m_pDynCols[i].index = i;
			m_pDynCols[i].offs = 0;
		}
		break;
	case LOCAL_DYNSET_TYPESPEC3:
		FreeDynCol();
		m_nColCount = 19;
		XMALLOC(m_pDynCols,COLEX*,m_nColCount * sizeof(COLEX)) /// 메모리를 확보한다.
		for( int i = 0 ; i < m_nColCount ; i ++)
		{
			STRCPY(m_pDynCols[i].ColInfo.szDbName ,typeSpec3[i].db,128);
			STRCPY(m_pDynCols[i].ColInfo.szTableName,typeSpec3[i].table,128);
			STRCPY(m_pDynCols[i].ColInfo.szColumnName,typeSpec3[i].column,128);
			m_pDynCols[i].ColInfo.nType = tableSpec2[i].type;
			m_pDynCols[i].type = -1;
			m_pDynCols[i].max = 0;
			m_pDynCols[i].lenp = NULL;
			m_pDynCols[i].valp = NULL;
			m_pDynCols[i].index = i;
			m_pDynCols[i].offs = 0;
		}
		break;
	case LOCAL_DYNSET_COLSPEC2:
		FreeDynCol();
		m_nColCount = 18;
		XMALLOC(m_pDynCols,COLEX*,m_nColCount * sizeof(COLEX)) /// 메모리를 확보한다.
		for( int i = 0 ; i < m_nColCount ; i ++)
		{
			STRCPY(m_pDynCols[i].ColInfo.szDbName ,colSpec2[i].db,128);
			STRCPY(m_pDynCols[i].ColInfo.szTableName,colSpec2[i].table,128);
			STRCPY(m_pDynCols[i].ColInfo.szColumnName,colSpec2[i].column,128);
			m_pDynCols[i].ColInfo.nType = tableSpec2[i].type;
			m_pDynCols[i].type = -1;
			m_pDynCols[i].max = 0;
			m_pDynCols[i].lenp = NULL;
			m_pDynCols[i].valp = NULL;
			m_pDynCols[i].index = i;
			m_pDynCols[i].offs = 0;
		}
		break;
	case LOCAL_DYNSET_COLSPEC3:
		FreeDynCol();
		m_nColCount = 18;
		XMALLOC(m_pDynCols,COLEX*,m_nColCount * sizeof(COLEX)) /// 메모리를 확보한다.
		for( int i = 0 ; i < m_nColCount ; i ++)
		{
			STRCPY(m_pDynCols[i].ColInfo.szDbName ,colSpec3[i].db,128);
			STRCPY(m_pDynCols[i].ColInfo.szTableName,colSpec3[i].table,128);
			STRCPY(m_pDynCols[i].ColInfo.szColumnName,colSpec3[i].column,128);
			m_pDynCols[i].ColInfo.nType = tableSpec2[i].type;
			m_pDynCols[i].type = -1;
			m_pDynCols[i].max = 0;
			m_pDynCols[i].lenp = NULL;
			m_pDynCols[i].valp = NULL;
			m_pDynCols[i].index = i;
			m_pDynCols[i].offs = 0;
		}
		break;
	default:
		return FALSE;
	}
	FixupDynCols();
	return TRUE;
}

int CSTMTEx::GetRowDataold(SQLUSMALLINT col, SQLSMALLINT otype, SQLPOINTER val, SQLINTEGER len, SQLLEN *lenp, int partial)
{
    char *data, valdummy[16];
    SQLLEN dummy;
    int valnull = 0;
    int type = otype;

    if (!lenp) 
	{
		lenp = &dummy;
    }
    if (col >= m_nColCount) 
	{
		SetState( -1, "invalid column", g_ov3 ? "07009" : "S1002");
		return SQL_ERROR;
    }
    if (retr_data != SQL_RD_ON) 
	{
		return SQL_SUCCESS;
    }
	type = mapdeftype(type, m_pDynCols[col].ColInfo.nType, m_pDynCols[col].ColInfo.nNosign ? 1 : 0, nowchar[0]);
#if (defined(_WIN32) || defined(_WIN64)) && defined(WINTERFACE)
    /* MS Access hack part 3 (map SQL_C_DEFAULT to SQL_C_CHAR) */
    if (type == SQL_C_WCHAR && otype == SQL_C_DEFAULT) 
	{
		type = SQL_C_CHAR;
    }
#endif
	data = & m_pDynCols[col].szVal[1];
    if (!val) 
	{
		valnull = 1;
		val = (SQLPOINTER) valdummy;
    }
    if (data == NULL) 
	{
		*lenp = SQL_NULL_DATA;
		switch (type) 
		{
		case SQL_C_UTINYINT:
		case SQL_C_TINYINT:
		case SQL_C_STINYINT:
#ifdef SQL_BIT
		case SQL_C_BIT:
#endif
			*((char *) val) = 0;
			break;
		case SQL_C_USHORT:
		case SQL_C_SHORT:
		case SQL_C_SSHORT:
			*((short *) val) = 0;
			break;
		case SQL_C_ULONG:
		case SQL_C_LONG:
		case SQL_C_SLONG:
			*((SQLINTEGER *) val) = 0;
			break;
	#ifdef SQL_BIGINT
		case SQL_C_SBIGINT:
		case SQL_C_UBIGINT:
			*((SQLBIGINT *) val) = 0;
			break;
	#endif
		case SQL_C_FLOAT:
			*((float *) val) = 0;
			break;
		case SQL_C_DOUBLE:
			*((double *) val) = 0;
			break;
		case SQL_C_BINARY:
		case SQL_C_CHAR:
			*((char *) val) = '\0';
			break;
	#ifdef WCHARSUPPORT
		case SQL_C_WCHAR:
			*((SQLWCHAR *) val) = '\0';
			break;
	#endif
	#ifdef SQL_C_TYPE_DATE
		case SQL_C_TYPE_DATE:
	#endif
		case SQL_C_DATE:
			memset((DATE_STRUCT *) val, 0, sizeof (DATE_STRUCT));
			break;
	#ifdef SQL_C_TYPE_TIME
		case SQL_C_TYPE_TIME:
	#endif
		case SQL_C_TIME:
			memset((TIME_STRUCT *) val, 0, sizeof (TIME_STRUCT));
			break;
	#ifdef SQL_C_TYPE_TIMESTAMP
		case SQL_C_TYPE_TIMESTAMP:
	#endif
		case SQL_C_TIMESTAMP:
			memset((TIMESTAMP_STRUCT *) val, 0, sizeof (TIMESTAMP_STRUCT));
			break;
		default:
			return SQL_ERROR;
		}
    } 
	else 
	{
		char *endp = NULL;
#if defined(_WIN32) || defined(_WIN64)
#ifdef SQL_BIGINT
		char endc;
#endif
#endif

		switch (type) 
		{
		case SQL_C_UTINYINT:
		case SQL_C_TINYINT:
		case SQL_C_STINYINT:
			*((char *) val) = (char)strtol(data, &endp, 0);
			if (endp && endp == data) 
			{
				*lenp = SQL_NULL_DATA;
			} 
			else 
			{
				*lenp = sizeof (char);
			}
			break;
	#ifdef SQL_BIT
		case SQL_C_BIT:
			*((char *) val) = getbool(data);
			*lenp = sizeof (char);
			break;
	#endif
		case SQL_C_USHORT:
		case SQL_C_SHORT:
		case SQL_C_SSHORT:
			*((short *) val) = strtol(data, &endp, 0);
			if (endp && endp == data) {
			*lenp = SQL_NULL_DATA;
			} else {
			*lenp = sizeof (short);
			}
			break;
		case SQL_C_ULONG:
		case SQL_C_LONG:
		case SQL_C_SLONG:
			*((SQLINTEGER *) val) = strtol(data, &endp, 0);
			if (endp && endp == data) {
			*lenp = SQL_NULL_DATA;
			} else {
			*lenp = sizeof (SQLINTEGER);
			}
			break;
	#ifdef SQL_BIGINT
		case SQL_C_UBIGINT:
	#if defined(_WIN32) || defined(_WIN64)
			if (sscanf(data, "%I64u%c", (SQLUBIGINT *) val, &endc) != 1) {
			*lenp = SQL_NULL_DATA;
			} else {
			*lenp = sizeof (SQLUBIGINT);
			}
	#else
	#ifdef __osf__
			*((SQLUBIGINT *) val) = strtoul(data, &endp, 0);
	#else
			*((SQLUBIGINT *) val) = strtoull(data, &endp, 0);
	#endif
			if (endp && endp == data) {
			*lenp = SQL_NULL_DATA;
			} else {
			*lenp = sizeof (SQLUBIGINT);
			}
	#endif
			break;
		case SQL_C_SBIGINT:
	#if defined(_WIN32) || defined(_WIN64)
			if (sscanf(data, "%I64d%c", (SQLBIGINT *) val, &endc) != 1) {
			*lenp = SQL_NULL_DATA;
			} else {
			*lenp = sizeof (SQLBIGINT);
			}
	#else
	#ifdef __osf__
			*((SQLBIGINT *) val) = strtol(data, &endp, 0);
	#else
			*((SQLBIGINT *) val) = strtoll(data, &endp, 0);
	#endif
			if (endp && endp == data) {
			*lenp = SQL_NULL_DATA;
			} else {
			*lenp = sizeof (int);
			}
	#endif
			break;
	#endif
		case SQL_C_FLOAT:
			*((float *) val) = ln_strtod(data, &endp);
			if (endp && endp == data) {
			*lenp = SQL_NULL_DATA;
			} else {
			*lenp = sizeof (float);
			}
			break;
		case SQL_C_DOUBLE:
			*((double *) val) = ln_strtod(data, &endp);
			if (endp && endp == data) {
			*lenp = SQL_NULL_DATA;
			} else {
			*lenp = sizeof (double);
			}
			break;
		case SQL_C_BINARY: {
			int dlen, offs = 0;
			char *bin;

			if (valnull) {
			XFREE(bincache);
			binlen = 0;
			goto doCHAR;
			}
			if (data == bincell) {
			if (bincache) {
				bin = bincache;
				dlen = binlen;
			} else {
				goto doCHAR;
			}
			} else {
			char *dp;
			int i;

			XFREE(bincache);
			dp = data;
			dlen = strlen(dp);
			bincell = dp;
			binlen = 0;
			if (!(dp[0] == 'x' || dp[0] == 'X') || dp[1] != '\'' ||
				dp[dlen - 1] != '\'') {
				goto doCHAR;
			}
			dlen -= 2;
			dp += 2;
			dlen = dlen / 2;
			//bin = xmalloc(dlen);
			XMALLOC(bin,char *,dlen)
			bincache = bin;
			if (!bin) {
				return NoMemory();
			}
			binlen = dlen;
			memset(bincache, 0, dlen);
			for (i = 0; i < dlen; i++) {
				char *x;
				int v;

				if (!*dp || !(x = (char*)strchr(xdigits, *dp))) {
				goto converr;
				}
				v = x - xdigits;
				bin[i] = (v >= 16) ? ((v - 6) << 4) : (v << 4);
				++dp;
				if (!*dp || !(x = (char*)strchr(xdigits, *dp))) {
	converr:
				XFREE(bincache);
				binlen = 0;
				SetState( -1, "conversion error",
					g_ov3 ? "HY000" : "S1000");
				return SQL_ERROR;
				}
				v = x - xdigits;
				bin[i] |= (v >= 16) ? (v - 6) : v;
				++dp;
			}
			bin = bincache;
			}
			if (partial && len && m_bBind) {
			if (m_pDynCols[col].offs >= dlen) {
				*lenp = 0;
				if (!dlen && m_pDynCols[col].offs == dlen) {
				m_pDynCols[col].offs = 1;
				return SQL_SUCCESS;
				}
				m_pDynCols[col].offs = 0;
				return SQL_NO_DATA;
			}
			offs = m_pDynCols[col].offs;
			dlen -= offs;
			}
			if (val && len) {
			memcpy(val, bin + offs, min(len, dlen));
			}
			if (len < 1) {
			*lenp = dlen;
			} else {
			*lenp = min(len, dlen);
			if (*lenp == len && *lenp != dlen) {
				*lenp = SQL_NO_TOTAL;
			}
			}
			if (partial && len && m_bBind) {
			if (*lenp == SQL_NO_TOTAL) {
				*lenp = dlen;
				m_pDynCols[col].offs += len;
				SetState( -1, "data right truncated", "01004");
				if (m_pDynCols[col].lenp) {
				*m_pDynCols[col].lenp = dlen;
				}
				return SQL_SUCCESS_WITH_INFO;
			}
			m_pDynCols[col].offs += *lenp;
			}
			if (*lenp == SQL_NO_TOTAL) {
			*lenp = dlen;
			SetState( -1, "data right truncated", "01004");
			return SQL_SUCCESS_WITH_INFO;
			}
			break;
		}
		doCHAR:
	#ifdef WCHARSUPPORT
		case SQL_C_WCHAR:
	#endif
		case SQL_C_CHAR: 
			{
				int dlen = strlen(data);
	#ifdef WCHARSUPPORT
				SQLWCHAR *ucdata = NULL;
				SQLCHAR *cdata = (SQLCHAR *) data;
	#endif

	#if (defined(_WIN32) || defined(_WIN64)) && defined(WINTERFACE)
			/* MS Access hack part 2 (reserved error -7748) */
				if (!valnull &&
				(m_pDynCols == statSpec2PEx || m_pDynCols == statSpec3PEx) &&
				type == SQL_C_WCHAR) 
				{
					if (len > 0 && len <= sizeof (SQLWCHAR)) 
					{
						((char *) val)[0] = data[0];
						memset((char *) val + 1, 0, len - 1);
						*lenp = 1;
						return SQL_SUCCESS;
					}
				}
	#endif

	#ifdef WCHARSUPPORT
			if (type == SQL_C_WCHAR) 
			{
				ucdata = uc_from_utf(cdata, dlen);
				if (!ucdata) 
				{
					return NoMemory();
				}
				//dlen = uc_strlen(ucdata) * sizeof (SQLWCHAR);
			}
	#if defined(_WIN32) || defined(_WIN64)
			else if (*oemcp && type == SQL_C_CHAR) 
			{
				ucdata = (SQLWCHAR *) utf_to_wmb((char *) cdata, dlen);
				if (!ucdata) 
				{
					return NoMemory();
				}
				cdata = (SQLCHAR *) ucdata;
				dlen = strlen((char *) cdata);
			}
	#endif
	#else
			doz = (type == SQL_C_CHAR) ? 1 : 0;
	#endif
			if (partial && len && m_bBind) {
			if (m_pDynCols[col].offs >= dlen) 
			{
	#ifdef WCHARSUPPORT
				XFREE(ucdata);
	#endif
				*lenp = 0;
				if (val) 
				{
	#ifdef WCHARSUPPORT
					if (type == SQL_C_WCHAR) 
					{
						((SQLWCHAR *) val)[0] = 0;
					} 
					else 
					{
						((char *) val)[0] = '\0';
					}
	#else
				((char *) val)[0] = '\0';
	#endif
				}
				if (!dlen && m_pDynCols[col].offs == dlen) 
				{
					m_pDynCols[col].offs = 1;
					return SQL_SUCCESS;
				}
				m_pDynCols[col].offs = 0;
				return SQL_NO_DATA;
			}
			}
			if (val && !valnull && len) 
			{
	#ifdef WCHARSUPPORT
				if (type == SQL_C_WCHAR) 
				{
					memset((char*)val,0,len * sizeof(SQLWCHAR)); // 이거참 꼭 이렇게 초기화 까지 해야 할까나?
					wcsncpy((wchar_t*)val,ucdata,min((int)len,(int)wcslen(ucdata)));
				} 
				else 
				{
					STRCPY((char*)val,(char*)cdata,len);
				}
	#else
			strncpy(val, data + offs, len - doz);
	#endif
			}
			if (valnull || len < 1) 
			{
				*lenp = dlen;
			} 
			
			if (len && !valnull) 
			{
	#ifdef WCHARSUPPORT
	#else
			((char *) val)[zlen] = '\0';
	#endif
			}
	#ifdef WCHARSUPPORT
			XFREE(ucdata);
	#endif
			if (partial && len && m_bBind) 
			{
			if (*lenp == SQL_NO_TOTAL) 
			{
				*lenp = dlen;
				m_pDynCols[col].offs += len;
				SetState( -1, "data right truncated", "01004");
				if (m_pDynCols[col].lenp) 
				{
					*m_pDynCols[col].lenp = dlen;
				}
				return SQL_SUCCESS_WITH_INFO;
			}
			m_pDynCols[col].offs += *lenp;
			}
			if (*lenp == SQL_NO_TOTAL) {
			*lenp = dlen;
			SetState( -1, "data right truncated", "01004");
			return SQL_SUCCESS_WITH_INFO;
			}
			break;
		}
	#ifdef SQL_C_TYPE_DATE
		case SQL_C_TYPE_DATE:
	#endif
		case SQL_C_DATE:
			if (str2date(data, (DATE_STRUCT *) val) < 0) {
			*lenp = SQL_NULL_DATA;
			} else {
			*lenp = sizeof (DATE_STRUCT);
			}
			break;
	#ifdef SQL_C_TYPE_TIME
		case SQL_C_TYPE_TIME:
	#endif
		case SQL_C_TIME:
			if (str2time(data, (TIME_STRUCT *) val) < 0) {
			*lenp = SQL_NULL_DATA;
			} else {
			*lenp = sizeof (TIME_STRUCT);
			}
			break;
	#ifdef SQL_C_TYPE_TIMESTAMP
		case SQL_C_TYPE_TIMESTAMP:
	#endif
		case SQL_C_TIMESTAMP:
			if (str2timestamp(data, (TIMESTAMP_STRUCT *) val) < 0) {
			*lenp = SQL_NULL_DATA;
			} else {
			*lenp = sizeof (TIMESTAMP_STRUCT);
			}
			switch (m_pDynCols[col].prec) {
			case 0:
			((TIMESTAMP_STRUCT *) val)->fraction = 0;
			break;
			case 1:
			((TIMESTAMP_STRUCT *) val)->fraction /= 100000000;
			((TIMESTAMP_STRUCT *) val)->fraction *= 100000000;
			break;
			case 2:
			((TIMESTAMP_STRUCT *) val)->fraction /= 10000000;
			((TIMESTAMP_STRUCT *) val)->fraction *= 10000000;
			break;
			}
			break;
		default:
			return SQL_ERROR;
		}
    }
    return SQL_SUCCESS;
}



int CSTMTEx::BindCol(SQLUSMALLINT col, SQLSMALLINT type,
	   SQLPOINTER val, SQLLEN max, SQLLEN *lenp)
{
	m_bBind = TRUE;
	int sz = 0;
    --col;
    if (type == SQL_C_DEFAULT) 
	{
		type = mapdeftype(type, m_pDynCols[col].type, 0,
				  nowchar[0] || nowchar[1]);
    }
    switch (type) 
	{
    case SQL_C_LONG:
    case SQL_C_ULONG:
    case SQL_C_SLONG:
		sz = sizeof (SQLINTEGER);
		break;
    case SQL_C_TINYINT:
    case SQL_C_UTINYINT:
    case SQL_C_STINYINT:
		sz = sizeof (SQLCHAR);
		break;
    case SQL_C_SHORT:
    case SQL_C_USHORT:
    case SQL_C_SSHORT:
		sz = sizeof (short);
		break;
    case SQL_C_FLOAT:
		sz = sizeof (SQLFLOAT);
		break;
    case SQL_C_DOUBLE:
		sz = sizeof (SQLDOUBLE);
		break;
    case SQL_C_TIMESTAMP:
		sz = sizeof (SQL_TIMESTAMP_STRUCT);
		break;
    case SQL_C_TIME:
		sz = sizeof (SQL_TIME_STRUCT);
		break;
    case SQL_C_DATE:
		sz = sizeof (SQL_DATE_STRUCT);
		break;
    case SQL_C_CHAR:
	break;
#ifdef WINTERFACE
    case SQL_C_WCHAR:
		break;
#endif
#ifdef SQL_C_TYPE_DATE
    case SQL_C_TYPE_DATE:
		sz = sizeof (SQL_DATE_STRUCT);
		break;
#endif
#ifdef SQL_C_TYPE_TIME
    case SQL_C_TYPE_TIME:
		sz = sizeof (SQL_TIME_STRUCT);
		break;
#endif
#ifdef SQL_C_TYPE_TIMESTAMP
	case SQL_C_TYPE_TIMESTAMP:
		sz = sizeof (SQL_TIMESTAMP_STRUCT);
		break;
#endif
#ifdef SQL_BIT
    case SQL_C_BIT:
		sz = sizeof (SQLCHAR);
		break;
#endif
    case SQL_C_BINARY:
		break;
#ifdef SQL_BIGINT
    case SQL_C_SBIGINT:
    case SQL_C_UBIGINT:
		sz = sizeof (SQLBIGINT);
		break;
#endif
    default:
		if (val == NULL) 
		{
			/* fall through, unbinding column */
			break;
		}
		SetState( -1, "invalid type %d", "HY003", type);
		return SQL_ERROR;
    }
    if (val == NULL) 
	{
		/* unbind column */
		m_pDynCols[col].type = -1;
		m_pDynCols[col].max = 0;
		m_pDynCols[col].lenp = NULL;
		m_pDynCols[col].valp = NULL;
		m_pDynCols[col].offs = 0;
	} 
	else 
	{
		if (sz == 0 && max < 0) 
		{
			SetState( -1, "invalid length", "HY090");
			return SQL_ERROR;
		}
		m_pDynCols[col].bBind = TRUE;
		m_pDynCols[col].type = type;
		m_pDynCols[col].max = (sz == 0) ? max : sz;
		m_pDynCols[col].lenp = lenp;
		m_pDynCols[col].valp = val;
		m_pDynCols[col].offs = 0;
		if (lenp) 
		{
			*lenp = 0;
		}
    }
    return SQL_SUCCESS; 
}

/**
	@brief Fetch Bind
*/
int CSTMTEx::DoFetchBind()
{
	for (int i = 0; i < m_nColCount; i++) 
	{
		//BINDCOL *b = &m_pBindCols[i];
		if( m_pDynCols[i].bBind == FALSE)
		{
			continue;
		}
		else if( SQL_ERROR == GetRowData((SQLUSMALLINT)i, m_pDynCols[i].type, m_pDynCols[i].valp,
			m_pDynCols[i].max, m_pDynCols[i].lenp, 0))
		{
			return SQL_ERROR;
		}
	}
	return SQL_SUCCESS;
}

BOOL CSTMTEx::GetTypeInfo(SQLSMALLINT sqltype)  
{
	int nRowCount;
#ifdef SQL_LONGVARCHAR
    nRowCount = (sqltype == SQL_ALL_TYPES) ? 13 : 1;
#else
    nRowCount = (sqltype == SQL_ALL_TYPES) ? 12 : 1;
#endif
    if (sqltype == SQL_ALL_TYPES) 
	{
#ifdef WINTERFACE
	nRowCount += 2;
#ifdef SQL_WLONGVARCHAR
	nRowCount += 2;
#endif
#endif
    }
    if (sqltype == SQL_ALL_TYPES) {
	nRowCount += 2;
#ifdef SQL_BIT
	nRowCount += 1;
#endif
#ifdef SQL_BIGINT
	nRowCount += 1;
#endif
    }
#ifdef MEMORY_DEBUG
    s->rowfree = xfree__;
#else
#endif
    if (sqltype == SQL_ALL_TYPES) 
	{
		
    } 
	else 
	{
		switch (sqltype) 
		{
		case SQL_CHAR:
			//mktypeinfo(s, 1, asize, "char", SQL_CHAR, 10);
			break;
		case SQL_VARCHAR:
			//typeinfo(s, 1, asize, "varchar", SQL_VARCHAR, 1);
			break;
		case SQL_TINYINT:
			//mktypeinfo(s, 1, asize, "tinyint", SQL_TINYINT, 2);
			break;
		case SQL_SMALLINT:
			//mktypeinfo(s, 1, asize, "smallint", SQL_SMALLINT, 3);
			break;
		case SQL_INTEGER:
			//mktypeinfo(s, 1, asize, "integer", SQL_INTEGER, 4);
			break;
		case SQL_FLOAT:
			//mktypeinfo(s, 1, asize, "float", SQL_FLOAT, 5);
			break;
		case SQL_DOUBLE:
			//mktypeinfo(s, 1, asize, "double", SQL_DOUBLE, 6);
			break;
	#ifdef SQL_TYPE_DATE
		case SQL_TYPE_DATE:
			//mktypeinfo(s, 1, asize, "date", SQL_TYPE_DATE, 25);
			break;
	#endif
		case SQL_DATE:
			//mktypeinfo(s, 1, asize, "date", SQL_DATE, 7);
			break;
	#ifdef SQL_TYPE_TIME
		case SQL_TYPE_TIME:
			//mktypeinfo(s, 1, asize, "time", SQL_TYPE_TIME, 26);
			break;
	#endif
		case SQL_TIME:
			//mktypeinfo(s, 1, asize, "time", SQL_TIME, 8);
			break;
	#ifdef SQL_TYPE_TIMESTAMP
		case SQL_TYPE_TIMESTAMP:
			//mktypeinfo(s, 1, asize, "timestamp", SQL_TYPE_TIMESTAMP, 27);
			break;
	#endif
		case SQL_TIMESTAMP:
			//ktypeinfo(s, 1, asize, "timestamp", SQL_TIMESTAMP, 9);
			break;
	#ifdef SQL_LONGVARCHAR
		case SQL_LONGVARCHAR:
			//mktypeinfo(s, 1, asize, "longvarchar", SQL_LONGVARCHAR, 12);
			break;
	#endif
		case SQL_VARBINARY:
			//mktypeinfo(s, 1, asize, "varbinary", SQL_VARBINARY, 30);
			break;
		case SQL_LONGVARBINARY:
			//mktypeinfo(s, 1, asize, "longvarbinary", SQL_LONGVARBINARY, 31);
			break;
	#ifdef SQL_BIT
		case SQL_BIT:
			//mktypeinfo(s, 1, asize, "bit", SQL_BIT, 29);
			break;
	#endif
	#ifdef SQL_BIGINT
		case SQL_BIGINT:
			//mktypeinfo(s, 1, asize, "bigint", SQL_BIGINT, 28);
			break;
	#endif
	#ifdef WINTERFACE
	#ifdef SQL_WCHAR
		case SQL_WCHAR:
			//mktypeinfo(s, 1, asize, "wchar", SQL_WCHAR, 18);
			break;
	#endif
	#ifdef SQL_WVARCHAR
		case SQL_WVARCHAR:
			//mktypeinfo(s, 1, asize, "wvarchar", SQL_WVARCHAR, 19);
			break;
	#endif
	#ifdef SQL_WLONGVARCHAR
		case SQL_WLONGVARCHAR:
			//mktypeinfo(s, 1, asize, "longwvarchar", SQL_WLONGVARCHAR, 20);
			break;
	#endif
	#endif
		default:
			nRowCount = 0;
		}
    }
	return Prepare(GETTYPESQL);
}

int CSTMTEx::GetTables(
	  SQLCHAR *cat, SQLSMALLINT catLen,
	  SQLCHAR *schema, SQLSMALLINT schemaLen,
	  SQLCHAR *table, SQLSMALLINT tableLen,
	  SQLCHAR *type, SQLSMALLINT typeLen)
{
	SQLRETURN ret;
    int ncols, asize, rc, size, npatt;
    char *errp = NULL, *sql, tname[512];
    char *where = "(type = 'table' or type = 'view')";
    if (type && (typeLen > 0 || typeLen == SQL_NTS) && type[0] == '%') 
	{
		/*
		memset(s->rows, 0, sizeof (char *) * size);
		s->ncols = asize;
		s->rows[s->ncols + 0] = "";
		s->rows[s->ncols + 1] = "";
		s->rows[s->ncols + 2] = "";
		s->rows[s->ncols + 3] = "TABLE";
		s->rows[s->ncols + 5] = "";
		s->rows[s->ncols + 6] = "";
		s->rows[s->ncols + 7] = "";
		s->rows[s->ncols + 8] = "VIEW";
		s->nrows = 2;
		s->rowp = -1;
		*/
		return SQL_SUCCESS;
    }
    if (cat && (catLen > 0 || catLen == SQL_NTS) && cat[0] == '%') 
	{
		table = NULL;
		goto doit;
    }
    if (schema && (schemaLen > 0 || schemaLen == SQL_NTS) &&schema[0] == '%') 
	{
		if ((!cat || catLen == 0 || !cat[0]) &&
	    (!table || tableLen == 0 || !table[0])) 
		{
			table = NULL;
			goto doit;
		}
    }
    if (type && (typeLen > 0 || typeLen == SQL_NTS) && type[0] != '\0') 
	{
		char tmp[256], *t;
		int with_view = 0, with_table = 0;
		if (typeLen == SQL_NTS) 
		{
			strncpy(tmp, (char *) type, sizeof (tmp));
			tmp[sizeof (tmp) - 1] = '\0';
		} 
		else 
		{
			int len = min(sizeof (tmp) - 1, typeLen);
		    strncpy(tmp, (char *) type, len);
		    tmp[len] = '\0';
		}
		t = tmp;
		while (*t) 
		{
			*t = TOLOWER(*t);
			t++;
		}
		t = tmp;
		unescpat(t);
		while (t) 
		{
			if (t[0] == '\'') 
			{
				++t;
			}
			if (strncmp(t, "table", 5) == 0) 
			{
				with_table++;
			} 
			else if (strncmp(t, "view", 4) == 0) 
			{
				with_view++;
			}
			t = strchr(t, ',');
			if (t) 
			{
				++t;	
			}
		}
		if (with_view && with_table) 
		{
	    /* where is already preset */
		} 
		else if (with_view && !with_table) 
		{
			where = "type = 'view'";
		} 
		else if (!with_view && with_table) 
		{
			where = "type = 'table'";
		} 
		else 
		{
			return SQL_SUCCESS;
		}
    }
doit:
    if (!table) 
	{
		size = 1;
		tname[0] = '%';
    } 
	else 
	{
		if (tableLen == SQL_NTS) 
		{
			size = sizeof (tname) - 1;
		} 
		else 
		{
			size = min(sizeof (tname) - 1, tableLen);
		}
		strncpy(tname, (char *) table, size);
    }
    tname[size] = '\0';
    npatt = unescpat(tname);
#if defined(_WIN32) || defined(_WIN64)
    sql = sqlite3_mprintf("select %s as 'TABLE_QUALIFIER', "
			  "%s as 'TABLE_OWNER', "
			  "tbl_name as 'TABLE_NAME', "
			  "upper(type) as 'TABLE_TYPE', "
			  "NULL as 'REMARKS' "
			  "from sqlite_master where %s "
			  "and tbl_name %s %Q",
			  m_pDbc->m_nXcelqrx ? "''" : "NULL",
			  m_pDbc->m_nXcelqrx ? "'main'" : "NULL",
			  where,
			  npatt ? "like" : "=", tname);
#else
    sql = sqlite3_mprintf("select NULL as 'TABLE_QUALIFIER', "
			  "NULL as 'TABLE_OWNER', "
			  "tbl_name as 'TABLE_NAME', "
			  "upper(type) as 'TABLE_TYPE', "
			  "NULL as 'REMARKS' "
			  "from sqlite_master where %s "
			  "and tbl_name %s %Q", where,
			  npatt ? "like" : "=", tname);
#endif
	
	if( TRUE == Prepare(sql))
	{
		sqlite3_free(sql);
		return SQL_SUCCESS;
	}
	else
	{
		sqlite3_free(sql);
		return SQL_ERROR;
	}
}

/**
	@brief 주어진 테이블에 대한 컬럼 정보를 얻는다.
*/
BOOL CSTMTEx::GetClolumn2(char *pszTABLE_NAME)
{
	char szTABLE_QUALIFIER[50];
	char szTABLE_OWNER[50];
	char szTABLE_NAME[255];
	char szCOLUMN_NAME[255];
	int  nDATA_TYPE;
	char szTYPE_NAME[50];
	int  nPRECISION;
	int  nLENGTH;
	int  nSCALE;
	int  nRADIX;
	int  nNULLABLE;
	char szREMARKS[50];
	char szCOLUMN_DEF[50];
	int  nSQL_DATA_TYPE;
	int  nSQL_DATETIME_SUB;
	int  nCHAR_OCTET_LENGTH;
	int  nORDINAL_POSITION;
	char szIS_NULLABLE[50];
	char szSQL[1024];
	sprintf(szSQL,"PRAGMA table_info(%Q)", pszTABLE_NAME);
	if( FALSE == Prepare(szSQL))
		return FALSE;
	const char *pszVal;
	while( Fetch())
	{
		for( int i = 0 ; i < m_nColCount ; i++)
		{
			pszVal = & m_pDynCols[i].szVal[1];
		}
	}
	return TRUE;

}

/**
	@brief 인덱스에 따른 실데이터 포인터를 리턴한다.
*/
const char * CSTMTEx::GetVal(int nIndex)
{
	return & m_pDynCols[nIndex].szVal[1];
}

/**
	@brief 데이터를 넣는다.
*/
void  CSTMTEx::AddData(const char *pVal)
{
	/*
	if( m_pInsData == NULL)
	{
		return;
	}
	
	m_pInsData->pVal = (char*)malloc(strlen(pVal)+1);
	if( strlen(pVal) > 0 )
	{
		STRCPY(m_pInsData->pVal, pVal,strlen(pVal));
	}
	else
		memset( m_pInsData->pVal,0,strlen(pVal));
	DATA_VAL * pData;
	XMALLOC(pData,DATA_VAL*,sizeof(DATA_VAL));
	m_pInsData->pNext = pData;
	m_pInsData = pData;
	*/
	char * pIns = (char*)malloc(strlen(pVal)+1);
	if( strlen(pVal) > 0 )
	{
		STRCPY(pIns, pVal,strlen(pVal)+1);
	}
	else
		memset( pIns,0,strlen(pVal)+1);
	m_arrVal.Add(pIns);
}
